package com.j2eefast.flowable.bpm.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;
import com.j2eefast.common.core.page.Query;
import com.j2eefast.common.core.utils.PageUtil;
import com.j2eefast.common.core.utils.ResponseData;
import com.j2eefast.common.core.utils.ToolUtil;
import com.j2eefast.flowable.bpm.cmd.processinstance.DeleteFlowableProcessInstanceCmd;
import com.j2eefast.flowable.bpm.entity.CompleteTaskEntity;
import com.j2eefast.flowable.bpm.entity.ProcessInstanceEntity;
import com.j2eefast.flowable.bpm.entity.RevokeProcessEntity;
import com.j2eefast.flowable.bpm.entity.StartProcessInstanceEntity;
import com.j2eefast.flowable.bpm.enums.CommentTypeEnum;
import com.j2eefast.flowable.bpm.service.FlowableProcessInstanceService;
import com.j2eefast.flowable.bpm.utils.BpmConstant;
import com.j2eefast.framework.utils.Constant;
import com.j2eefast.framework.utils.UserUtils;
import org.flowable.bpmn.model.*;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.flowable.engine.runtime.Execution;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p></p>
 *
 * @author: yhli
 * @date: 2020-04-22 16:10
 * @web: http://www.j2eefast.com
 * @version: 1.0.1
 */
@Service
public class FlowableProcessInstanceServiceImpl extends BaseProcessService implements FlowableProcessInstanceService {

    @Override
    public ResponseData startProcessInstanceByKey(StartProcessInstanceEntity params) {
        if (ToolUtil.isNotEmpty(params.getProcessDefinitionKey())
                && ToolUtil.isNotEmpty(params.getBusinessKey())
                && ToolUtil.isNotEmpty(params.getSystemSn())) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(params.getProcessDefinitionKey())
                    .latestVersion().singleResult();
            if (processDefinition != null && processDefinition.isSuspended()) {
                return ResponseData.error("70001", "此流程已经挂起,请联系系统管理员!");
            }
            /**
             * 1、设置变量
             * 1.1、设置提交人字段为空字符串让其自动跳过
             * 1.2、设置可以自动跳过
             * 1.3、汇报线的参数设置
             */
            //1.1、设置提交人字段为空字符串让其自动跳过
            params.getVariables().put(BpmConstant.FLOW_SUBMITTER_VAR, "");
            //1.2、设置可以自动跳过
            params.getVariables().put(BpmConstant.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
            params.getVariables().put("fallbackToDefaultTenant","");
            params.getVariables().put(BpmConstant.SALE_CODE,params.getSaleCode());
            params.getVariables().put(BpmConstant.IS_WAI,params.getIsWai());
            // TODO 1.3、汇报线的参数设置
            //2、当我们流程创建人和发起人
            String creator = params.getCreator();
            if (ToolUtil.isEmpty(creator)) {
                creator = params.getCurrentUserCode();
                params.setCreator(creator);
            }
            //3.启动流程
            identityService.setAuthenticatedUserId(creator);
            ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                    .processDefinitionKey(params.getProcessDefinitionKey().trim())
                    .name(params.getFormName().trim())
                    .businessKey(params.getBusinessKey().trim())
                    .variables(params.getVariables())
                    .tenantId(params.getSystemSn().trim())
                    .start();

            //4.添加审批记录
            String taskId = flowableCommentService.findTaskInstId(processInstance.getProcessInstanceId());
            nextFlowNode("next",processInstance.getProcessInstanceId());
            //3.1修改执行人 其实我这里就相当于签收了
            //taskService.setAssignee(userTask.getId(), UserUtils.getUserInfo().getId().toString());
            this.addComment(taskId, params.getCurrentUserCode(), processInstance.getProcessInstanceId(),
                    CommentTypeEnum.TJ.toString(), params.getFormName() + "提交");

            //5.TODO 推送消息数据
            return ResponseData.success(processInstance);
        } else {
            return ResponseData.error("70001", "请填写 这三个字段 ProcessDefinitionKey,BusinessKey,SystemSn");
        }
    }

    /**
     * 获取任务节点
     *
     * @param node   查询节点选择
     * @param processInstanceId 任务id
     */
    public void nextFlowNode(String node, String processInstanceId) {
        //当前任务信息
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        taskService.setAssignee(task.getId(), UserUtils.getUserInfo().getId().toString());
//        //获取流程发布Id信息
//        String definitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();
//
//        //获取bpm对象
//        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
//
//        //传节点定义key 获取当前节点
//        FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
//
//        //输出连线
//        List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
//        List<SequenceFlow> newsequenceFlows = null;
//        //遍历返回下一个节点信息
//        for (SequenceFlow outgoingFlow : outgoingFlows) {
//            //类型自己判断
//            FlowElement source = outgoingFlow.getTargetFlowElement();
//            List<SequenceFlow> sequenceFlows = null;
//            if (source instanceof org.flowable.bpmn.model.Task) {
//                sequenceFlows = ((org.flowable.bpmn.model.Task) source).getOutgoingFlows();
//            } else if (source instanceof Gateway) {
//                sequenceFlows = ((Gateway) source).getOutgoingFlows();
//            } else if (source instanceof SubProcess) {
//                sequenceFlows = ((SubProcess) source).getOutgoingFlows();
//            } else if (source instanceof StartEvent) {
//                sequenceFlows = ((StartEvent) source).getOutgoingFlows();
//            } else if (source instanceof EndEvent) {
//                sequenceFlows = ((EndEvent) source).getOutgoingFlows();
//            }
//            newsequenceFlows=sequenceFlows;
//            break;
//        }
//        List<SequenceFlow> anewsequenceFlows= newsequenceFlows;
    }

    private void setExclusiveGateway(FlowElement targetFlow) {
        //排他网关，获取连线信息
        List<SequenceFlow> targetFlows = ((ExclusiveGateway) targetFlow).getOutgoingFlows();
        for (SequenceFlow sequenceFlow : targetFlows) {
            //目标节点信息
            FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
            if (targetFlowElement instanceof UserTask) {
                // do something
            } else if (targetFlowElement instanceof EndEvent) {
                // do something
            } else if (targetFlowElement instanceof ServiceTask) {
                // do something
            } else if (targetFlowElement instanceof ExclusiveGateway) {
                //递归寻找
                setExclusiveGateway(targetFlowElement);
            } else if (targetFlowElement instanceof SubProcess) {
                // do something
            }
        }
    }

    private Object getVal(ValuedDataObject valued, String valStr) {
        Object val = null;
        if (valued instanceof StringDataObject) {
            val = valStr;
        } else if (valued instanceof IntegerDataObject) {
            val = Integer.parseInt(valStr);
        } else if (valued instanceof LongDataObject) {
            val = Long.parseLong(valStr);
        } else if (valued instanceof DoubleDataObject) {
            val = Double.parseDouble(valStr);
        } else if (valued instanceof BooleanDataObject) {
            val = Boolean.parseBoolean(valStr);
        } else if (valued instanceof DateDataObject) {
            val = com.j2eefast.flowable.bpm.utils.DateUtil.string2date(valStr, com.j2eefast.flowable.bpm.utils.DateUtil.YYYY_MM_DD_HH_MM_SS);
        }
        return val;
    }


    @Override
    public PageUtil findPage(Map<String, Object> params) {
        String formName = (String) params.get("formName");
        String starterId = (String) params.get("starterId");
        String starter = (String) params.get("starter");
        Page<ProcessInstanceEntity> page = processInstanceMapper.findPage(new Query<ProcessInstanceEntity>(params).getPage(),
                StrUtil.nullToDefault(starterId, ""),
                StrUtil.nullToDefault(formName, ""),
                StrUtil.nullToDefault(starter, ""),
                (String) params.get(Constant.SQL_FILTER));
        return new PageUtil(page);
    }

    @Override
    public ResponseData deleteProcessInstanceById(String processInstanceId) {
        long count = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).count();
        if (count > 0) {
            DeleteFlowableProcessInstanceCmd cmd = new DeleteFlowableProcessInstanceCmd(processInstanceId, "删除流程实例", true);
            managementService.executeCommand(cmd);
            return ResponseData.success("删除成功");
        } else {
            historyService.deleteHistoricProcessInstance(processInstanceId);
            return ResponseData.success("删除成功");
        }
    }

    @Override
    public ResponseData suspendOrActivateProcessInstanceById(String processInstanceId, Integer suspensionState) {
        if (suspensionState == 1) {
            runtimeService.suspendProcessInstanceById(processInstanceId);
            return ResponseData.success("挂起成功");
        } else {
            runtimeService.activateProcessInstanceById(processInstanceId);
            return ResponseData.success("激活成功");
        }
    }

    /**
     * 发起人任务撤回
     *
     * @param revoke
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseData revokeProcess(RevokeProcessEntity revoke) {
        if (ToolUtil.isNotEmpty(revoke.getProcessInstanceId())) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(revoke.getProcessInstanceId()).singleResult();
            if (processInstance != null) {
                //1.添加撤回意见
                this.addComment(revoke.getUserId(), revoke.getProcessInstanceId(), CommentTypeEnum.CH.toString(), revoke.getMessage());
                //2.设置提交人
                runtimeService.setVariable(revoke.getProcessInstanceId(), "initiator", processInstance.getStartUserId());
                //3.执行撤回
                String activityId = flowableActinstService.getBpmActivityId(processInstance.getStartUserId(), revoke.getProcessInstanceId());
                //4.删除运行和历史的节点信息
                this.deleteActivity(activityId, revoke.getProcessInstanceId());
                //5.执行跳转
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(revoke.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                this.moveExecutionsToSingleActivityId(executionIds, activityId);
                return ResponseData.success("撤回成功!");
            } else {
                return ResponseData.error("撤回失败!");
            }
        } else {
            return ResponseData.error("流程实例id不能为空!");
        }
    }

    /**
     * 流程收回/驳回
     * @param params
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseData flowTackbacks(CompleteTaskEntity params) {
        return this.flowTackback(params);
    }

    /**
     * 启动流程
     *
     * @param procDefKey 流程定义KEY
     * @param businessId 业务表编号
     * @return 流程实例ID
     */
    @Override
    public String startProcess(String procDefKey, String businessId, String userName, String deptId, Set<String> roleCodes) {
        return startProcess(procDefKey, businessId, Maps.newHashMap(), userName, deptId, roleCodes);
    }

    /**
     * 启动流程
     *
     * @param procDefKey 流程定义KEY
     * @param businessId 业务表编号
     * @param vars       流程变量
     * @return 流程实例ID
     */
    @Override
    public String startProcess(String procDefKey, String businessId, Map<String, Object> vars, String userName, String deptId, Set<String> roleCodes) {
        // 设置流程变量
        if (vars == null) {
            vars = Maps.newHashMap();
        }
        //设置流程发起人-可以在后面流程中驳回重新办理
        vars.put(BpmConstant.FLOW_SUBMITTER, userName);
//        vars.put(BpmConstant.FLOW_SUBMITTER_VAR, deptId);
//        vars.put(BpmConstant.FLOWABLE_SKIP_EXPRESSION_ENABLED, roleCodes);
        // 启动流程
        return runtimeService.startProcessInstanceByKey(procDefKey, businessId, vars).getId();
    }
}
